﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web;
using ICSharpCode.SharpZipLib.Zip;
using System.Text.RegularExpressions;
using System.IO;

namespace Possan.ZipFolders
{
	public class Module : IHttpModule
	{
		public void Dispose()
		{
		}

		FolderConfig config;

		public void Init(HttpApplication context)
		{
			log4net.Config.XmlConfigurator.Configure();
			config = System.Configuration.ConfigurationSettings.GetConfig("Possan.ZipFolders") as FolderConfig;
			context.BeginRequest += new EventHandler(context_BeginRequest);
			log4net.ILog l = log4net.LogManager.GetLogger("Possan.ZipFolders");
			l.Debug("ZipFolders module initialized");
		}

		void context_BeginRequest(object sender, EventArgs e)
		{
			HttpContext c = HttpContext.Current;
			string lp = c.Request.Url.LocalPath;


			bool keeprunning = true;
			for (int j = 0; j < config.Folders.Count && keeprunning; j++)
			{
				Match m = Regex.Match(lp, config.Folders[j].UrlMatch, RegexOptions.IgnoreCase | RegexOptions.Singleline);
				if (m.Success)
				{
					bool ok = false;

					int ttl_seconds = config.Folders[j].CacheDuration;

					string zipfile = config.Folders[j].ZipFilePath;
					string realzipfilepath = c.Server.MapPath(zipfile);

					string inzip_basepath = config.Folders[j].ZipBasePath;
					string inzip_relpath = m.Groups[1].Value;
					string inzip_full = inzip_basepath + m.Groups[1].Value;

					if (inzip_full.StartsWith("/"))
						inzip_full = inzip_full.Substring(1);

					string ext = "";
					int lpp = inzip_full.LastIndexOf(".");
					if (lpp != -1)
						ext = inzip_full.Substring(lpp + 1).ToLower();

					if (File.Exists(realzipfilepath))
					{
						using (ZipFile zf = new ZipFile(realzipfilepath))
						{
							ZipEntry ze = zf.GetEntry(inzip_full);
							if (ze != null)
							{
								if (ze.CanDecompress)
								{
									c.Response.StatusCode = 200;
									
									// TODO: do a better mimetype resolution

									if (ext == "gif") c.Response.ContentType = "image/gif";
									else if (ext == "jpg" || ext == "jpeg") c.Response.ContentType = "image/jpeg";
									else if (ext == "png") c.Response.ContentType = "image/png";
									else if (ext == "htm" || ext == "html") c.Response.ContentType = "text/html";
                                    else if (ext == "txt" || ext == "text") c.Response.ContentType = "text/plain";
                                    else if (ext == "css" ) c.Response.ContentType = "text/css";
                                    else if (ext == "js" || ext == "json") c.Response.ContentType = "text/javascript";
									else c.Response.ContentType = "application/x-octet-stream";

									c.Response.AddHeader("Content-Disposition", "inline");

									c.Response.Cache.SetExpires(DateTime.Now.ToUniversalTime().AddSeconds(ttl_seconds));
									c.Response.Cache.SetCacheability(HttpCacheability.Public);
									c.Response.Cache.SetMaxAge(new TimeSpan(0, 0, 0, ttl_seconds));
									c.Response.Cache.SetRevalidation(HttpCacheRevalidation.None);
                                    c.Response.Cache.SetETag("zcrc"+ze.Crc.ToString());

									System.IO.Stream ins = zf.GetInputStream(ze);
									byte[] buf = new byte[16384];
									bool cont = true;
									while (cont)
									{
										int read = ins.Read(buf, 0, buf.Length);
										cont = (read > 0);
										if (cont)
											c.Response.OutputStream.Write(buf, 0, read);
									}

									ins.Close();
									ok = true;
								}
								else
								{
									c.Response.StatusCode = 404;
									c.Response.Write("CANT DECOMPRESS: " + inzip_full);
								}
							}
							else
							{
								c.Response.StatusCode = 404;
								c.Response.Write("ZIP ENTRY NOT FOUND: " + inzip_full);
							}
							zf.Close();
						}
					}
					else
					{
						c.Response.StatusCode = 404;
						c.Response.Write("ZIP NOT FOUND: " + zipfile);
					}

					c.Response.End();

					keeprunning = false;

					if (!ok)
					{
						log4net.ILog l = log4net.LogManager.GetLogger("Possan.ZipFolders");

						l.Error("Zip-request: " + realzipfilepath + " (full: " + inzip_full + ") ");

					}
				}
			}

		}
	}
}
